package coins;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List; //import startegies.Strategy;

import com.dukascopy.api.IBar;
import com.dukascopy.api.IStrategy;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.Period;
import com.dukascopy.api.system.ISystemListener;

/**
 * Created by IntelliJ IDEA. User: kushal Date: 11/23/10 Time: 9:48 PM To change
 * this template use File | Settings | File Templates.
 */
public class SimulationSession implements ISystemListener {
	private String user;
	private String password;
	private Date startDate;
	private Date endDate;
	public double initialDeposit;
	private int leverage;
	public double finalAmount;
	private boolean started;
	private String url = "https://www.dukascopy.com/client/demo/jclient/jforex.jnlp";
	private Connection ticksConnection;
	private Connection askBarsConnection;
	private Connection bidBarsConnection;
	private ResultSet ticks;
	private ResultSet askBars;
	private ResultSet bidBars;

	private long from;
	private long to;
	private HashMap<IStrategy, TesterClient> strategyMap;
	private Period period;
	public boolean useFileSystem;

	public SimulationSession(String url, String user, String password,
			Date startDate, Date endDate, double initialDeposit, int leverage,
			List<IStrategy> strategies, Period period) throws Exception {
		strategyMap = new HashMap<IStrategy, TesterClient>();
		for (IStrategy s : strategies)
		{
			TesterClient tempClient = new TesterClient(period);
			tempClient.setInitialDeposit(Instrument.EURUSD.getSecondaryCurrency(), initialDeposit);
			tempClient.setLeverage(leverage);			
			strategyMap.put(s, tempClient);
		}
		this.user = user;
		this.password = password;
		this.startDate = startDate;
		this.endDate = endDate;
		this.initialDeposit = initialDeposit;
		this.finalAmount = initialDeposit;
		this.leverage = leverage;
		this.url = url;
		this.period = period;
	}

	public void start() throws Exception, ClassNotFoundException,
			InstantiationException, IllegalAccessException {

		Calendar fromDate = Calendar.getInstance(java.util.TimeZone
				.getTimeZone("UTC"));
		Calendar toDate = Calendar.getInstance(java.util.TimeZone
				.getTimeZone("UTC"));

		fromDate.setTime(startDate);
		toDate.setTime(endDate);
		from = fromDate.getTime().getTime();
		to = toDate.getTime().getTime();
		if (!useFileSystem)
			loadData();
		else
			loadDataFromCSV();
	}

	private void loadData() throws SQLException, Exception {

		System.out.println("Connecting...");

		ticksConnection = DriverManager.getConnection(url, user, password);
		askBarsConnection = DriverManager.getConnection(url, user, password);
		bidBarsConnection = DriverManager.getConnection(url, user, password);

		System.out.println("Connected");
		// Start the strategies here
		for (IStrategy strategy : strategyMap.keySet())
			strategyMap.get(strategy).startStrategy(strategy);

		System.out.println(Calendar.getInstance().getTime());
		Statement ticksStmt = ticksConnection.createStatement(
				java.sql.ResultSet.TYPE_FORWARD_ONLY,
				java.sql.ResultSet.CONCUR_READ_ONLY);
		ticksStmt.setFetchSize(Integer.MIN_VALUE);

		Statement askStmt = askBarsConnection.createStatement(
				java.sql.ResultSet.TYPE_FORWARD_ONLY,
				java.sql.ResultSet.CONCUR_READ_ONLY);
		ticksStmt.setFetchSize(Integer.MIN_VALUE);

		Statement bidStmt = bidBarsConnection.createStatement(
				java.sql.ResultSet.TYPE_FORWARD_ONLY,
				java.sql.ResultSet.CONCUR_READ_ONLY);
		ticksStmt.setFetchSize(Integer.MIN_VALUE);

		// Load data using from and to time
		ticks = ticksStmt
				.executeQuery("select Time, ask, bid, askvolume, bidvolume from TICK where Time >= "
						+ (from) + " and Time <= " + (to) + "");

		askBars = askStmt
				.executeQuery("select Time, open, high, low, close from "
						+ period.name() + "_ASK" + " where Time >= " + (from)
						+ " and Time <= " + (to) + "");

		bidBars = bidStmt
				.executeQuery("select Time, open, high, low, close from "
						+ period.name() + "_BID" + " where Time >= " + (from)
						+ " and Time <= " + (to) + "");

		System.out
				.println("select Time, ask, bid, askvolume, bidvolume from Ticks where Time >= "
						+ (from) + " and Time <= " + (to) + "");
		// Process each of the ticks
		IBar askBar = null, bidBar = null;
		if (askBars.next())
			askBar = new Bar(askBars.getLong("Time"),
					askBars.getDouble("open"), askBars.getDouble("high"),
					askBars.getDouble("low"), askBars.getDouble("close"));
		if (bidBars.next())
			bidBar = new Bar(bidBars.getLong("Time"),
					bidBars.getDouble("open"), bidBars.getDouble("high"),
					bidBars.getDouble("low"), bidBars.getDouble("close"));

		ResultSet set = ticks;
		while (set.next()) {
			for (TesterClient client : strategyMap.values()) {
				Tick tick = new Tick(ticks.getLong("Time"), ticks
						.getDouble("ask"), ticks.getDouble("bid"), ticks
						.getDouble("askvolume"), ticks.getDouble("bidvolume"));
				if ((tick.getTime() - period.getInterval()) >= askBar.getTime()) {
					while (askBars.next() && bidBars.next()) {
						client.onBar(Instrument.EURUSD, period, askBar, bidBar);
						askBar = new Bar(askBars.getLong("Time"), askBars
								.getDouble("open"), askBars.getDouble("high"),
								askBars.getDouble("low"), askBars
										.getDouble("close"));
						bidBar = new Bar(bidBars.getLong("Time"), bidBars
								.getDouble("open"), bidBars.getDouble("high"),
								bidBars.getDouble("low"), bidBars
										.getDouble("close"));
						if ((tick.getTime() - period.getInterval()) >= askBar
								.getTime())
							continue;
						else
							break;
					}
				}
				client.onTick(tick);
			}
		}
		ticksStmt.close();

		for (IStrategy strategy : strategyMap.keySet()) {
			strategyMap.get(strategy).stopStrategy(0);
			strategyMap.get(strategy).createReport(
					new File(strategy.toString() + ".txt"));
		}

		System.out.println(Calendar.getInstance().getTime());
	}

	private void loadDataFromCSV() throws SQLException, Exception {

		// Start the strategies here
		for (IStrategy strategy : strategyMap.keySet())
			strategyMap.get(strategy).startStrategy(strategy);

		System.out.println(Calendar.getInstance().getTime());

		// Get the daily index for ticks
		BufferedReader ticks_index = new BufferedReader(new FileReader(
				"data/TICK_index.csv"));
		String line, tickPrevLine = null, bidPrevLine = null, askPrevLine = null;
		while ((line = ticks_index.readLine()) != null) {
			if (Long.parseLong(line.substring(0, 13)) >= from) {
				if (tickPrevLine == null)
					tickPrevLine = line;
				break;
			}
			tickPrevLine = line;
		}
		ticks_index.close();

		// Get the daily index for askbars
		BufferedReader asks_index = new BufferedReader(new FileReader("data/"
				+ period.name() + "_ASK_index.csv"));
		while ((line = asks_index.readLine()) != null) {
			if (Long.parseLong(line.substring(0, 13)) >= from) {
				if (askPrevLine == null)
					askPrevLine = line;
				break;
			}
			askPrevLine = line;
		}
		asks_index.close();

		// Get the daily index for ticks
		BufferedReader bids_index = new BufferedReader(new FileReader("data/"
				+ period.name() + "_BID_index.csv"));
		while ((line = bids_index.readLine()) != null) {
			if (Long.parseLong(line.substring(0, 13)) >= from) {
				if (bidPrevLine == null)
					bidPrevLine = line;
				break;
			}
			bidPrevLine = line;
		}
		bids_index.close();

		// return if no data found
		if (askPrevLine == null || bidPrevLine == null || tickPrevLine == null) {
			System.out.println("NO data found");
			return;
		}

		// Open ticks data and seek to correct position
		BufferedReader ticks = new BufferedReader(new FileReader(
				"data/TICK.csv"), 500000);
		String tickLine = null, bidLine = null, askLine = null;
		ticks.skip(Long.parseLong(tickPrevLine.split(",")[1]));
		while ((tickLine = ticks.readLine()) != null)
			if (Long.parseLong(tickLine.substring(0, 13)) >= from)
				break;

		System.out.println("Seek completed to tick file "+ Calendar.getInstance().getTime());
		// Open ask data and seek to correct position
		BufferedReader askBars = new BufferedReader(new FileReader("data/"
				+ period.name() + "_ASK.csv"), 1000 * 50);
		askBars.skip(Long.parseLong(askPrevLine.split(",")[1]));
		while ((askLine = askBars.readLine()) != null)
			if (Long.parseLong(askLine.substring(0, 13)) >= from)
				break;
		// askBars.skip(askStart);

		// Open bid data and seek to correct position
		BufferedReader bidBars = new BufferedReader(new FileReader("data/"
				+ period.name() + "_BID.csv"), 1000 * 50);
		bidBars.skip(Long.parseLong(bidPrevLine.split(",")[1]));
		while ((bidLine = bidBars.readLine()) != null)
			if (Long.parseLong(bidLine.substring(0, 13)) >= from)
				break;
		// askBars.skip(bidStart);

		// Get first ask and bid bar
		IBar askBar = null, bidBar = null;
		if (askLine != null)
			askBar = stringToBar(askLine);
		if (bidLine != null)
			bidBar = stringToBar(bidLine);

		// loop thru ticks
		do {
			Tick tick = stringToTick(tickLine);
			if (tick.getTime() > to)
				break;
			for (TesterClient client : strategyMap.values()) {
				if ((tick.getTime() - period.getInterval()) >= askBar.getTime()) {
					while ((askLine = askBars.readLine()) != null
							&& (bidLine = bidBars.readLine()) != null) {
						client.onBar(Instrument.EURUSD, period, askBar, bidBar);
						askBar = stringToBar(askLine);
						bidBar = stringToBar(bidLine);
						if ((tick.getTime() - period.getInterval()) >= askBar
								.getTime())
							continue;
						else
							break;
					}
				}
				client.onTick(tick);
			}
		} while (((tickLine = ticks.readLine()) != null));

		// close readers
		ticks.close();
		askBars.close();
		bidBars.close();

		// stop strategies
		for (IStrategy strategy : strategyMap.keySet()) {
			strategyMap.get(strategy).stopStrategy(0);
			strategyMap.get(strategy).createReport(
					new File(strategy.toString() + ".txt"));
		}

		System.out.println(Calendar.getInstance().getTime());
	}

	private Tick stringToTick(String tick) {
		// String[] data = pattern.split(tick, 5);
		long l = Long.parseLong(tick.substring(0, 13));
		double d1 = Double.parseDouble(tick.substring(14, 22));
		double d2 = Double.parseDouble(tick.substring(23, 31));
		double d3 = Double.parseDouble(tick.substring(32, 44));
		double d4 = Double.parseDouble(tick.substring(45));
		return new Tick(l, d1, d2, d3, d4);
	}

	private IBar stringToBar(String bar) {
		long l = Long.parseLong(bar.substring(0, 13));
		double d1 = Double.parseDouble(bar.substring(14, 22));
		double d2 = Double.parseDouble(bar.substring(23, 31));
		double d3 = Double.parseDouble(bar.substring(32, 40));
		double d4 = Double.parseDouble(bar.substring(41));
		return new Bar(l, d1, d2, d3, d4);
	}

	/**
	 * Called on new strategy start
	 * 
	 * @param processId
	 *            id of the started strategy
	 */
	@Override
	public void onStart(long processId) {
		// TODO: Log varibles and initial data to db
		started = true;
	}

	/**
	 * Called on the strategy stop
	 * 
	 * @param processId
	 *            id of the strategy stopped
	 */
	@Override
	public void onStop(long processId) {
		System.exit(0);
	}

	/**
	 * Called on successfull connect
	 */
	@Override
	public void onConnect() {
	}

	/**
	 * Called on disconnect
	 */
	@Override
	public void onDisconnect() {

	}

	public boolean isRunning() {
		return started;
	}
}
